package com.wachulski.marcin.msc.experiment;

import java.util.ArrayList;

import com.wachulski.marcin.msc.pl.PLQuery;
import com.wachulski.marcin.msc.pl.PLScript;
import com.wachulski.marcin.msc.pl.PLSystemKB;
import com.wachulski.marcin.msc.utils.FilePaths;
import com.wachulski.marcin.msc.utils.KBWarehouse;
import com.wachulski.marcin.msc.utils.PLIRoutines;

import edu.isi.powerloom.Environment;
import edu.isi.powerloom.PLI;
import edu.isi.stella.Stella_Object;

public final class ExpEngine {
	private static final double ATOM_QUERY_TIMEOUT = 3.0;
	private static final double FULL_QUERY_TIMEOUT = 5.0;
	
	private PLSystemKB system = null;
	private String systemName = null;
	private Environment env = null;
	private String dataSizeStr = null;
	private boolean isCpl = false;
	
	public ExpEngine(PLSystemKB system, Environment env, int dataSize, boolean isCpl) {
		this.system = system;
		this.env = env;
		this.isCpl = isCpl;
		
		this.systemName = system.getName().split("_", 2)[1].toLowerCase();
		this.dataSizeStr = String.format("%05d", dataSize);
	}
	
	public void runExperiment() {
		PLScript script = new PLScript(systemName + "-system_Cpl_Test", 
				FilePaths.EXP_ROOT + "/" + systemName + "/exp_"+ systemName +"_" + dataSizeStr + ".plm");
		
		PLIRoutines.loadSystem(system, env);
		PLI.load(FilePaths.EXP_ROOT + "/" + systemName + "/data_" + systemName + "_" + dataSizeStr + ".plm", env);
		
		// test analysis - for experiment test results visualization
		ArrayList<ArrayList<Character>> qaResults = new ArrayList<ArrayList<Character>>();
		ArrayList<ArrayList<Double>> qtResults = new ArrayList<ArrayList<Double>>();
		ArrayList<Integer> posResults = new ArrayList<Integer>();
		ArrayList<Integer> uknResults = new ArrayList<Integer>();
		ArrayList<Integer> negResults = new ArrayList<Integer>();
		ArrayList<Double> timResults = new ArrayList<Double>();
		int longestRunSize = 0;
		qaResults.add(new ArrayList<Character>());
		qtResults.add(new ArrayList<Double>());
		
		// test statistics - for experiment test results calculation
		int theoremId = 1;
		int theoremQueryId = 1;
		int totalQueries = 0;
		int theoremPos = 0;
		int theoremUkn = 0;
		int theoremNeg = 0;
		int totalPrv = 0;
		int totalPos = 0;
		int totalUkn = 0;
		int totalNeg = 0;
		double theoremTime = 0;
		double totalTime = 0;
		
		String answerStr = null;
		String queryStr = null;
		Stella_Object queryRes = null;
		long startTime = 0;
		long stopTime = 0;
		double queryTime = 0.0;
		double queryTimeout = 0.0;
		
		ArrayList<PLQuery> queries = script.getQueries();
		
		// test statistics calculation
		for (PLQuery q : queries) {
			if (Integer.parseInt(q.getName().split("-")[0]) != theoremId) {
				PLIRoutines.clearAll(env);
				PLIRoutines.loadSystem(system, env);
				PLI.load(FilePaths.EXP_ROOT + "/" + systemName + "/data_" + systemName + "_" + dataSizeStr + ".plm", env);
				// progress status
				System.out.print("["+theoremId+"],");
				
				++theoremId;
				if ((theoremQueryId-1) > longestRunSize)
					longestRunSize = theoremQueryId-1;
				qaResults.add(new ArrayList<Character>());
				qtResults.add(new ArrayList<Double>());
				
				posResults.add(theoremPos);
				uknResults.add(theoremUkn);
				negResults.add(theoremNeg);
				totalPos += theoremPos;
				totalUkn += theoremUkn;
				totalNeg += theoremNeg;
				
				timResults.add(theoremTime / (theoremQueryId-1));
				totalTime += theoremTime;
				
				theoremQueryId = 1;
				theoremPos = 0;
				theoremUkn = 0;
				theoremNeg = 0;
				theoremTime = 0.0;
			}
			queryTimeout = (theoremQueryId == 1) ? FULL_QUERY_TIMEOUT : ATOM_QUERY_TIMEOUT;
			queryStr = "(ask " + q.getBody() + " :timeout "+ queryTimeout +")";
			
			startTime = System.currentTimeMillis();
			queryRes = PLI.sEvaluate(queryStr, KBWarehouse.KB_BASIC_BOUNDARY.getModulePath(), env);
			stopTime = System.currentTimeMillis();	
			queryTime = (stopTime - startTime) / 1000.0;
			qtResults.get(theoremId-1).add(queryTime);
			theoremTime += queryTime;
			
			answerStr = queryRes.toString();
			if (answerStr.equals("TRUE")) {
				qaResults.get(theoremId-1).add(' ');
				++theoremPos;				
				if (theoremQueryId == 1)
					++totalPrv;
			}
			else if (answerStr.equals("UNKNOWN")) {
				qaResults.get(theoremId-1).add('?');
				++theoremUkn;
			}
			else if (answerStr.equals("FALSE")) {
				qaResults.get(theoremId-1).add('X');
				++theoremNeg;
			}
			++theoremQueryId;
			++totalQueries;
		}
		if ((theoremQueryId-1) > longestRunSize)
			longestRunSize = theoremQueryId-1;
		posResults.add(theoremPos);
		uknResults.add(theoremUkn);
		negResults.add(theoremNeg);
		timResults.add(theoremTime / theoremPos);
		totalPos += theoremPos;
		totalUkn += theoremUkn;
		totalNeg += theoremNeg;
		totalTime = (totalTime + theoremTime) / totalPos;
		// progress status
		System.out.print("["+theoremId+"] - DONE!");
		
		// test results visualization
		String dhLine = "===============================================" +
				"====================================================================";
		String shLine = "-----------------------------------------------" +
				"--------------------------------------------------------------------";
		int numTheorems = qaResults.size();
		System.out.println("\n" + dhLine + "\n" + "   Completeness Experiment Results: " +
				systemName + "-system [data: " + dataSizeStr + "] \n" + dhLine);
		System.out.format("%-8s", "QueryId");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%4s", "T" + String.valueOf(i+1));
		System.out.println("\n" + shLine);
		if (isCpl) {
			for (int i=0; i < longestRunSize; ++i) {
				System.out.format("%8d", i+1);
				for (int j=0; j < numTheorems; ++j)
					if (i < qaResults.get(j).size())
						System.out.format("%4s",  qaResults.get(j).get(i));
					else
						System.out.format("%4s", " ");
				System.out.println();
			}
			System.out.println(shLine);
		}
		System.out.format("%8s", "TotalPos");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%4s", posResults.get(i));
		System.out.println();
		System.out.format("%8s", "TotalUnk");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%4s", uknResults.get(i));
		System.out.println();
		System.out.format("%8s", "TotalNeg");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%4s", negResults.get(i));
		System.out.println();
		System.out.format("%8s", "TotalQrs");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%4s", posResults.get(i) + uknResults.get(i) + negResults.get(i));
		System.out.println("\n" + shLine);
		System.out.format("%s:%6d (%3.2f)\n", systemName + "-system Total Prv.", totalPrv, (double)totalPrv/theoremId);
		System.out.format("%s:%6d (%3.2f)\n", systemName + "-system Total Pos.", totalPos, (double)totalPos/totalQueries);
		System.out.format("%s:%6d (%3.2f)\n", systemName + "-system Total Unk.", totalUkn, (double)totalUkn/totalQueries);
		System.out.format("%s:%6d (%3.2f)\n", systemName + "-system Total Neg.", totalNeg, (double)totalNeg/totalQueries);
		System.out.format("%s:%6d\n", systemName + "-system Total Qrs ", totalQueries);
		System.out.println(dhLine + '\n');
		
		System.out.println(dhLine + "\n" + "   Time Measurement Results: " +
				systemName + "-system [data: " + dataSizeStr + "]\n" + dhLine);
		System.out.format("%8s", "QueryId");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%7s", "T" + String.valueOf(i+1));
		System.out.println("\n" + shLine);
		if (isCpl) {
			for (int i=0; i < longestRunSize; ++i) {
				System.out.format("%8d", i+1);
				for (int j=0; j < numTheorems; ++j)
					if (i < qtResults.get(j).size())
						System.out.format("%7.3f",  qtResults.get(j).get(i));
					else
						System.out.format("%7s", " ");
				System.out.println();
			}
			System.out.println(shLine);
		}
		System.out.format("%8s", "AvgRTime");
		for (int i=0; i < numTheorems; ++i)
			System.out.format("%7.3f", timResults.get(i));
		System.out.println("\n" + shLine);
		System.out.format("%s:%7.3f\n", systemName + "-system Total Avg Resp. Time", totalTime);
		System.out.println(dhLine + '\n');
	}
}
